#include <gtest/gtest.h>

#include "helper/Result.h"
#include "helper/unittest/Tracer.h"

using namespace helper;

TEST(ResultTest, testResultDefault){
  {
    auto r = Result<>::OK();
    ASSERT_TRUE(r.isOK());
    ASSERT_EQ(0, r.ok());

    ASSERT_FALSE(r.isError());
    ASSERT_EQ(0, r.error());
  }

  {
    auto r = Result<>::Error(1);
    ASSERT_TRUE(r.isError());
    ASSERT_EQ(1, r.error());

    ASSERT_FALSE(r.isOK());
    ASSERT_EQ(-1, r.ok());
  }
}

TEST(ResultTest, testResultString) {
  {
    auto r = Result<std::string>::OK();
    ASSERT_TRUE(r.isOK());
    ASSERT_EQ(0, r.ok());

    ASSERT_FALSE(r.isError());
    ASSERT_EQ("", r.error());
  }
  {
    auto r = Result<std::string>::Error("fd is not exist");
    ASSERT_TRUE(r.isError());
    ASSERT_EQ("fd is not exist", r.error());

    ASSERT_FALSE(r.isOK());
    ASSERT_EQ(-1, r.ok());
  }
}

TEST(ResultTest, testCompare){
  auto ok = Result<>::OK() ;
  auto err = Result<>::Error(1);

  {
    ASSERT_NE(ok, err);
  }
  {
    auto ok1 = Result<>::OK();
    ASSERT_EQ(ok, ok1);
  }
  {
    auto err1 = Result<>::Error(1);
    ASSERT_EQ(err, err1);

    auto err2 = Result<>::Error(2);
    ASSERT_NE(err, err2);
  }
}

TEST(ResultTest, testAssign){
  auto ok = Result<>::OK();
  auto ok1 = Result<>::OK();
  ASSERT_EQ(ok, ok1);
  ok = ok1;
  ASSERT_EQ(ok, ok1);

  Result<> r(ok);
  ASSERT_EQ(r, ok);


  auto err = Result<>::Error(1);
  auto err1 = Result<>::Error(2);
  ASSERT_NE(err, err1);
  err = err1;
  ASSERT_EQ(err, err1);

  Result<> e(err);
  ASSERT_EQ(e, err);
}

TEST(ResultTest, testOutput){
  auto o = Result<>::OK();
  std::cout << o << std::endl;
}

TEST(ResultTest, testBool){
  auto o = Result<>::OK();
  ASSERT_TRUE(o);
  ASSERT_FALSE(!o);
  auto e = Result<>::Error(1);
  ASSERT_FALSE(e);
  ASSERT_TRUE(!e);
}


TEST(IOResultTest, testMain){
  auto e = IOResult::Error(std::make_pair(-1, "fd is not found"));
  ASSERT_FALSE(e.isOK());
  ASSERT_TRUE(e.isError());
  ASSERT_FALSE(e);

  ASSERT_EQ(-1, e.error().first);
  ASSERT_EQ("fd is not found", e.error().second);

  auto  r = IOResult::OK();
  ASSERT_TRUE(r.isOK());
  ASSERT_FALSE(r.isError());
}


//OKCode extend unittest: 
//    Result.isOK() && result.oKInfo() in [1, 2, 3] support
TEST(ResultTest, testOKCode){
  auto r =  Result<>::OK().okInfo(1);
  switch(r.okInfo()) {
    case 1: 
      ASSERT_TRUE(r);
      ASSERT_TRUE(true);
      break; 
    default:
      ASSERT_FALSE(true);
      break;
  }
}

/*  
// use case, translation table  start

//1. r.and_then(..... return Result<>new_r);
if(r){
	do_somthing;
	return new_r;
}
return r

//2. r.or_else(..... return Result<>new_e)
if(r.isError()){
	do_somthing;
	return new_e;
}
return r;

//3. inspect
if(r.isOK()){
	do_somthing;
}
return r;

//4. inspect error
if(r.isError()){
	do_somthing;
}
return r;


//5. map 
if(r.isOK()) {
	return Result( op_ok(_o), _e);
}
return Result(_o, _e)

//6. map_err
if(r.isError()){
	return Result( _o, op_err(_e));
}
return Result(_o, _e);


//map_or;
if(r){
	return U(value);
}
else{
	return do_somthing(e);
}

// map_or_else(do(ok)... return U,  do(e).... return U);
if(r){
	return do_somthing(ok);
}
else{
	return do_somthing(e);
}

Translation  table end*/

// 1. inspect -> void op;  object ref;   return Result
// 2. and/or  create object; no type converted op; return Result
// 3. map/map_error create object;  customized op for type converter, return Result
// 4. map_or  create object;   no only return Result, return everything

// *********  Function Style Requirement  **********
//OK 1. and, and_then    no custom type convert, required:E -convertable-> F
//OK   1.1 and  Result<int, E> => Result<int, E>
//OK   1.2 and_then(op) Result<int> => Result<int>
//
//OK 2. or , or_else   E ---convertable-> F
//OK   2.1 or
//OK 2.2 or_else(op)
//
//OK 3. map	  type convert with op		Note:  Result -> Result     
//OK   3.2 map Result<T, E> => Result<U, E>, err untouched
//OK   3.1 map_err Result<T, E> => Result<T, F>, ok untouched
//
//OK 4. map_or  Result<> -> U 
//OK   4.1 map_or(U, U func(const int& ))    Result<>
//OK   4.2 map_or_else( U func(const int&), U func(const E &))  Result<> -> U
//
//OK 5. insepct 
//OK   5.1 inspect_error
//OK   5.2 inspect &&
//TODO: 
//   6. is_ok_and, is_err_and
//


// 1.1  and<F>;  
//  untouched error;  ok()--return-->given result
//  Result<int, E> => Result<int, F>
TEST(ResultTest, testAnd){
	auto expectOK = Result<long long>::OK();
	ASSERT_EQ(expectOK,		 Result<>::OK().andReturn<long long>(expectOK));
	auto expectError = Result<long long>::Error(1);
	ASSERT_EQ(expectError, Result<>::OK().andReturn<long long>(expectError));


	ASSERT_EQ(expectError,  Result<>::Error(1).andReturn<long long>(expectOK));
	auto expectError2 = Result<long long>::Error(2);
	ASSERT_EQ(expectError2, Result<>::Error(2).andReturn<long long>(expectOK));
}
//1.2 andThen<F> 
//  [useless], untouched error, ok()--run-->op
//  Result<int, E> => Result<int, F>
TEST(ResultTest, testAndThen){
  auto op_ok = [](const int & o) {
    std::cout << "ok clause" << std::endl;
    return Result<long long>::OK();
  };
  ASSERT_EQ(Result<long long>::OK(), Result<>::OK().andThen<long long>(op_ok));


  ASSERT_EQ(Result<long long>::Error(), Result<>::Error().andThen<long long>(op_ok));


  //TODO: think how to impl 
  //  Better use case
  /*
  r.andReturn<std::string>(Result<std::string>("hello"), std::string cvt(const int& e)); 
  r.orReturn<std::string>(Result<std::string>("hello"));

  r.andThen<std::string>([](const int & o) -> Result<std::string>);
  r.orElse<std::string>([](const E & e) -> Result<std::string>);

  Result<int>::OK()

  Result<std::string>(_o, (int)_e); => int => std::string ?



  map_or_else<std::string>(map_ok, map_error);
  if(r){ return map_ok(r.ok());} 
  else { return map_error(r.error());}
  */
}

//2 or<E> untouched ok, 
//  : if Error =>  return Result<int, F>
//    if OK()  => Result<int, F>(_o, _e);
TEST(ResultTest, testOr){
	auto expectOK = Result<long long>::OK();
	auto expectError = Result<long long>::Error(1);
  ASSERT_EQ(expectOK,		 Result<>::Error().orReturn<long long>(expectOK));
  ASSERT_EQ(expectError, Result<>::Error().orReturn<long long>(expectError));


	ASSERT_EQ(Result<long long>::OK(), Result<>::OK().orReturn<long long>(expectOK));
	ASSERT_EQ(Result<long long>::OK(), Result<>::OK().orReturn<long long>(expectError));

}
TEST(ResultTest, testOrElse){
  auto op_err = [](const int & e){
    std::cout << "error clause:" << e << std::endl;
    return Result<long long>::Error(e+1);
  };

	ASSERT_EQ(Result<long long>::Error(1),  Result<>::Error().orElse<long long>(op_err));
	ASSERT_EQ(Result<long long>::Error(2), Result<>::Error(1).orElse<long long>(op_err));
	ASSERT_EQ(Result<long long>::OK(), Result<>::OK().orElse<long long>(op_err));
	ASSERT_EQ(Result<long long>::OK(), Result<>::OK().orElse<long long>(op_err));
}


IOResult __open_fd_not_found(){
  return IOResult::Error(std::make_pair(-1, "fd is not found"));
}
TEST(ResultTest, useResult) {
  auto r = __open_fd_not_found();
  ASSERT_FALSE(r);
  ASSERT_FALSE(r.isOK());
}


//3.2 map_err Result<T, E> => Result<T, F>, ok untouched
TEST(ResultTest, testMapError){
  auto pre = Result<std::pair<int, std::string>>::OK();
  Result<int> last  = pre.mapError<int>(
    [](const auto & p) -> int { 
      return  p.first;
    });
  ASSERT_EQ(pre.ok(), last.ok());
  ASSERT_EQ(pre.error().first, last.error());
}

//  5.1 inspect_error
TEST(ResultTest, testInspectError){
  EXPECT.reset().method("inspect_error e:1");
  TRACER.reset();
  bool ok = Result<>::Error(1)
    .inspectError([](const int&e) {  
      TRACER.method(std::string("inspect_error e:") + std::to_string(e));
    }).isOK();
  ASSERT_EQ(EXPECT, TRACER);

  ASSERT_FALSE(ok);
}
//5.2 inspect
TEST(ResultTest, testInspect){
  EXPECT.reset().method("inspect o:0");
  TRACER.reset();
  bool ok = Result<>::OK()
    .inspect([](const int& o) {  
      TRACER.method(std::string("inspect o:") + std::to_string(o));
    }).isOK();
  ASSERT_EQ(EXPECT, TRACER);

  ASSERT_TRUE(ok);
}


// IOResult =map==> Result<int>
Result<> open()  {
  auto e = __open_fd_not_found()  //  Result<std::pair>
    .mapError<int>([](const auto & e) {  // std::pair -> int
      return e.first; 
    })
    .inspectError([](const int & e) { //  op_error(int)
      std::cerr << "open failed:" << e << std::endl;
    })
    .orElse<int>([](const int & e) {		//Result<int> op_error(int)
      std::cerr << "open failed:" << e << std::endl;
      return Result<>::Error(e);
    })
    ;
  return e;
}



// comment on   map_or_else(op_error, op_ok) -> U
// U op_error(const auto & e);
// U op_ok(const int & o);
Result<> open_origin(){
  IOResult e = __open_fd_not_found();
  if(e){
    std::cout << "open success" << std::endl;
    return Result<>::OK();
  }
  else{
    std::cerr << "open failed:" << e.error().first << std::endl;
    return Result<>::Error(e.error().first);
  }
}
// same as  ====>
Result<> open_rewrite(){
  return __open_fd_not_found()
    .mapOrElse<Result<>>(
    [](const auto & e){
      std::cerr << "open failed:" << e.first << std::endl;
      return Result<>::Error(e.first);
    },
    [](const int & o){
      std::cout << "open success" << std::endl;
      return Result<>::OK();
    })
    ;
}

TEST(ResultTest, testMapOr){

	std::string okString("hello");
	auto op_ok = [&okString](const int & o) -> std::string {
		std::cout << "this is ok:" << o << std::endl;
		return okString;
	};
	
	std::string errorString = "Error";
	ASSERT_EQ(okString, Result<>::OK().mapOr<std::string>(errorString, op_ok));
	ASSERT_EQ(errorString, Result<>::Error(1).mapOr<std::string>(errorString, op_ok));
}

TEST(ResultTest, testMapOrElse){
	std::string okString("hello");
	auto op_ok = [&okString](const int & o) -> std::string {
		std::cout << "this is ok:" << o << std::endl;
		return okString;
	};
	std::string errorString = "Error";
	auto op_error = [&errorString](const int & e) -> std::string {
		std::cout << "this is error:" << e << std::endl;
		return errorString;
	};

	ASSERT_EQ(okString, Result<>::OK().mapOrElse<std::string>(op_error, op_ok));
	ASSERT_EQ(errorString, Result<>::Error(1).mapOrElse<std::string>(op_error, op_ok));
}
